<script setup lang="ts">
import {computed, inject, type Ref, ref} from 'vue'
import MenuItem from './MenuItem.vue'
import {useRouter} from "vue-router";
import type SysPermission from "@/type/entity/sys-permission.ts";

const router = useRouter()

const emit = defineEmits(["select"])
const props = withDefaults(defineProps<{
  item: SysPermission
  level?: number
}>(), {
  level: 0
})

let item: SysPermission
if (props.item.children && props.item.children.length === 1) {
  item = props.item.children[0]
} else {
  item = props.item
}

//当前选择了哪个菜单项
const selectedKey = inject<Ref<string>>('selectedKey', ref(''))
//当前展开了哪些菜单项
const openKeys = inject<Ref<Set<string>>>('openKeys', ref(new Set()))
//点击了某个菜单项，触发的回调函数
const handleSelect = inject<(key: string) => void>('handleSelect', () => {
})

//判断该菜单是否展开
const open = computed(() => openKeys.value.has(item.permissionCode))

const toggle = () => {
  //如果openKeys包含了当前菜单项，那么就移除，否则添加
  if (open.value) {
    openKeys.value.delete(item.permissionCode)
  } else {
    openKeys.value.add(item.permissionCode)
  }

  //将当前选中的菜单项交给菜单管理组件处理
  handleSelect(item.permissionCode)

  // 如果是叶子节点（无children），跳转路由
  if (!hasChildren.value) {
    emit("select", item)
    router.push(item.permissionCode)
  }
}

const hasChildren = computed(() => {
  return item.children && item.children.length > 0
})
const isTop = computed(() => props.level === 0)
const isMiddle = computed(() => props.level > 0 && hasChildren.value)
const isLeaf = computed(() => !hasChildren.value)

//判断当前菜单项是否应该被选中
function isSelected(item: SysPermission): boolean {
  //如果当前菜单项的permissionCode等于selectedKey，那么就认为选中
  if (selectedKey.value === item.permissionCode) return true
  //如果当前菜单项的子菜单项被选中，那么就认为选中
  if (item.children) return item.children.some(isSelected)
  //否则当前菜单项没有选中
  return false
}

// 过渡钩子
function beforeEnter(el: Element) {
  const dom = el as HTMLElement
  dom.style.height = '0'
}

function enter(el: Element, done: () => void) {
  const dom = el as HTMLElement
  dom.style.transition = 'height 0.25s cubic-bezier(.4,0,.2,1)'
  void dom.offsetWidth // 强制重绘
  dom.style.height = dom.scrollHeight + 'px'

  // 动画完成后调用 done，确保 transition 结束后正确清理
  dom.addEventListener('transitionend', function handler(e) {
    if (e.propertyName === 'height') {
      dom.removeEventListener('transitionend', handler)
      done()
    }
  })
}

function afterEnter(el: Element) {
  const dom = el as HTMLElement
  dom.style.height = 'auto'
}

function beforeLeave(el: Element) {
  const dom = el as HTMLElement
  dom.style.height = dom.scrollHeight + 'px'
  void dom.offsetWidth
}

function leave(el: Element, done: () => void) {
  const dom = el as HTMLElement
  dom.style.transition = 'height 0.25s cubic-bezier(.4,0,.2,1)'
  dom.style.height = '0'

  dom.addEventListener('transitionend', function handler(e) {
    if (e.propertyName === 'height') {
      dom.removeEventListener('transitionend', handler)
      done()
    }
  })
}

function afterLeave(el: Element) {
  const dom = el as HTMLElement
  dom.style.height = '0'
}
</script>

<template>
  <div v-if="item.visible" class="menu-item" :class="{
    selected: isSelected(item),
    top: isTop,
    middle: isMiddle,
    leaf: isLeaf
  }">
    <!-- 可点击标签 -->
    <div class="item-label" @click="toggle" :class="{ hasChildren: hasChildren }">

      <span class="item-text"
            :style="{ paddingLeft: (level * 10) + 'px' }">
        <span v-if="isLeaf" class="dot"/>
        <component is="upload" v-if="!isLeaf" class="item-icon"/>
        {{ item.permissionName }}
      </span>

      <span v-if="hasChildren" class="arrow">
        <svg
            :style="{ transform: open ? 'rotate(0deg)' : 'rotate(180deg)', transition: 'transform 0.2s' }"
            width="18"
            height="18"
            viewBox="0 0 1024 1024"
            fill="none">
          <path d="M240 624l272-272 272 272" stroke="#444" stroke-width="60" stroke-linecap="round"
                stroke-linejoin="round" fill="none"/>
        </svg>
      </span>
    </div>

    <!-- 子菜单 -->
    <!-- 这里加transition动画 -->
    <transition
        name="slide-vertical"
        @before-enter="beforeEnter"
        @enter="enter"
        @after-enter="afterEnter"
        @before-leave="beforeLeave"
        @leave="leave"
        @after-leave="afterLeave">
      <div v-if="hasChildren" v-show="open" class="submenu">
        <MenuItem
            v-for="(child) in item.children"
            :key="child.id"
            :item="child"
            :level="level + 1"
            @select="(event:SysPermission) => $emit('select', event)"
        />
      </div>
    </transition>
  </div>
</template>

<style scoped>

.item-label {
  user-select: none;
  display: flex;
  align-items: center;
  cursor: pointer;
  font-size: 14px;
  height: 60px;
  padding: 8px;
  color: #000;
  margin: 1px 0;
  border-radius: 3px;
  transition: all 0.2s;
}

.submenu {
  overflow: hidden; /* 必须，否则动画溢出 */
}

.item-label:hover {
  background-color: #f0f7ff;
}

.menu-item.top.selected > .item-label {
  background: #ffffff;
  color: #1c7bfb;
  box-shadow: 0 0 15px 5px #f4f4f4;
}

.menu-item.leaf.selected > .item-label {
  color: #1c7bfb;
  background-color: #f0f7ff;
}

.menu-item.middle.selected > .item-label {
  color: #1c7bfb;
}

/* 子菜单前面的小圆点，默认隐藏 */
.dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: #409EFF;
  margin-right: 8px;
  opacity: 0;
  margin-left: 12px;
  transition: opacity 0.1s;
}

/* 当子菜单选中中，前面的小圆点就显示出来 */
.menu-item.leaf.selected .dot {
  opacity: 1;
}

.item-icon {
  display: inline-block;
  vertical-align: middle;
  margin-right: 8px;
}

/*菜单右边的箭头*/
.arrow {
  align-items: center;
  margin-left: auto;
  color: black;
}
</style>